using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Gj
{
    [Flags]
    public enum BlockDirection
    {
        Center,
        Top = 1 << 0,
        Bottom = 1 << 1,
        Left = 1 << 2,
        Right = 1 << 3,
        LeftTop = Top + Left,
        RightTop = Top + Right,
        LeftBottom = Bottom + Left,
        RightBottom = Bottom + Right
    }

    public struct Block
    {
        public Vector2Int Position;
        public Vector3Int Center;
        public Vector2Int Gap;
        public Vector2Int GapWrap;

        public int index => BlockTools.PositionIndex(Position);

        public int seed => index + Position.x + Position.y;

        public void Offset(int x, int z, ref Block block)
        {
            block.Gap = Gap;
            block.GapWrap = GapWrap;
            block.Position = new Vector2Int(Position.x + x * 2, Position.y + z * 2);
            block.Center.x = Center.x + x * Gap.x * 2;
            block.Center.z = Center.z + z * Gap.y * 2;
        }

        public Vector2Int GetOffset(ref Block block)
        {
            return new Vector2Int(Position.x - block.Position.x, Position.y - block.Position.y);
        }

        public RectInt ToRectInt()
        {
            return new RectInt(Center.x - Gap.x, Center.z - Gap.y, Gap.x * 2, Gap.y * 2);
        }

        public RectInt ToRectInt(int number)
        {
            return new RectInt(Center.x - Gap.x * number, Center.z - Gap.y * number, Gap.x * number * 2,
                Gap.y * number * 2);
        }

        public Rect ToRect()
        {
            return new Rect(Center.x - Gap.x, Center.z - Gap.y, Gap.x * 2, Gap.y * 2);
        }

        public Rect ToRect(int number)
        {
            return new Rect(Center.x - Gap.x * number, Center.z - Gap.y * number, Gap.x * number * 2,
                Gap.y * number * 2);
        }

        public void FromRect(RectInt rect)
        {
            Gap.x = rect.width / 2;
            Gap.y = rect.height / 2;
            Center.x = rect.xMin + Gap.x;
            Center.z = rect.yMin + Gap.y;
        }

        public void UpdatePosition(Vector2Int position)
        {
            Position = position;
            Center.x = Position.x * Gap.x;
            Center.z = Position.y * Gap.y;
        }

        public bool Contain(Vector3 position, bool wrap = false)
        {
            var g = wrap ? GapWrap.x : Gap.x;
            var flag = true;
            // 范围内的为正
            if (Center.x - g > position.x)
                flag = false;
            else if (Center.x + g <= position.x) flag = false;
            g = wrap ? GapWrap.y : Gap.y;
            // 范围外的为反
            if (Center.z - g > position.z)
                flag = false;
            else if (Center.z + g <= position.z) flag = false;

            return flag;
        }

        public bool Contain(Vector3 position, int number)
        {
            var g = Gap.x * number;
            var flag = true;
            // 范围内的为正
            if (Center.x - g > position.x)
                flag = false;
            else if (Center.x + g <= position.x) flag = false;
            g = Gap.y * number;
            // 范围外的为反
            if (Center.z - g > position.z)
                flag = false;
            else if (Center.z + g <= position.z) flag = false;

            return flag;
        }

        public void ToDirection(BlockDirection direction, ref Block block)
        {
            switch (direction)
            {
                case BlockDirection.Top:
                    Offset(0, 1, ref block);
                    break;
                case BlockDirection.Bottom:
                    Offset(0, -1, ref block);
                    break;
                case BlockDirection.Left:
                    Offset(-1, 0, ref block);
                    break;
                case BlockDirection.Right:
                    Offset(1, 0, ref block);
                    break;
                case BlockDirection.LeftTop:
                    Offset(-1, 1, ref block);
                    break;
                case BlockDirection.LeftBottom:
                    Offset(-1, -1, ref block);
                    break;
                case BlockDirection.RightTop:
                    Offset(1, 1, ref block);
                    break;
                case BlockDirection.RightBottom:
                    Offset(1, -1, ref block);
                    break;
                default:
                    Offset(0, 0, ref block);
                    break;
            }
        }

        public BlockDirection Direction(Vector3 position, int xVision, int zVision)
        {
            var direction = BlockDirection.Center;
            var e = xVision;
            if (Center.x - e >= position.x)
                direction |= BlockDirection.Left;
            else if (Center.x + e <= position.x) direction |= BlockDirection.Right;

            e = zVision;
            if (Center.z - e >= position.z)
                direction |= BlockDirection.Bottom;
            else if (Center.z + e <= position.z) direction |= BlockDirection.Top;

            return direction;
        }
    }

    public interface BlockNotice
    {
        IEnumerator OnKeep(Block block);
        IEnumerator OnUpdateBefore(Block block);
        IEnumerator OnAddBlock(Block block);
        IEnumerator OnRemoveBlock(Block block);
        IEnumerator OnUpdateAfter(Block block);
    }

    public class BlockManager
    {
        public delegate IEnumerator BlockDelegate(Block block);

        private BlockNotice _notice;
        public Block center;
        public Vector3 currentPosition;

        public List<Vector2Int> currentSet = new();
        private BlockDirection direction;
        public Vector2Int Gap;
        public Vector2Int GapWrap;
        private bool init;
        public Block Middle;
        public BlockDelegate OnAddBlockEvent;
        public BlockDelegate OnKeepEvent;
        public BlockDelegate OnRemoveBlockEvent;
        public BlockDelegate OnUpdateAfterEvent;

        public BlockDelegate OnUpdateBeforeEvent;
        public Vector3 position;
        private bool updateCenter;
        private bool updateInit;
        private bool updateSimple;
        private bool waitInit = false;
        private bool waitUpdate;

        public IEnumerator UpdateSimple(Vector3 tmpPosition)
        {
            // 在inline大小连续切换中，导致contain判断影响到最后一次更新条件
            currentPosition = tmpPosition;
            var keepCenter = false;
            if (init)
            {
                if (center.Contain(tmpPosition, true))
                {
                    keepCenter = true;
                    var d = center.Direction(tmpPosition, GapWrap.x - Gap.x, GapWrap.y - Gap.y);
                    if (d == direction
                        || d == BlockDirection.Left
                        || d == BlockDirection.Bottom
                        || d == BlockDirection.Right
                        || d == BlockDirection.Top
                        || d == BlockDirection.Center)
                    {
                        if (waitUpdate) yield break;
                        yield return _Keep();
                        yield break;
                    }
                }
            }
            else
            {
                if (Gap.x == 0 || Gap.y == 0 || GapWrap.x == 0 || GapWrap.y == 0) yield break;
                init = true;
            }

            var tmpCenter = center;
            if (!keepCenter) tmpCenter = Center(tmpPosition);

            if (waitUpdate) yield break;
            waitUpdate = true;
            updateSimple = true;

            var newSet = new List<Vector2Int>();
            var tmp = new Block { Gap = Gap, GapWrap = GapWrap };

            if (!keepCenter)
            {
                position = tmpPosition;
                center = tmpCenter;
            }

            direction = center.Direction(tmpPosition, 0, 0);
            yield return _UpdateBefore();

            BlockDirection[] directions;
            if (BlockTools.BlockSimpleMap.TryGetValue(direction, out directions))
                for (var i = 0; i < directions.Length; i++)
                    yield return _Add(tmp, directions[i], newSet);

            yield return _Replace(tmp, newSet);
            yield return _UpdateAfter();

            waitUpdate = false;
            if (updateInit)
                yield return UpdateInline();
            else
                yield return UpdateSimple(currentPosition);
        }

        public IEnumerator UpdateCenter(Vector3 tmpPosition)
        {
            // 在inline大小连续切换中，导致contain判断影响到最后一次更新条件
            currentPosition = tmpPosition;
            if (init)
            {
                if (center.Contain(tmpPosition, true))
                {
                    if (waitUpdate) yield break;
                    yield return _Keep();
                    yield break;
                }
            }
            else
            {
                if (Gap.x == 0 || Gap.y == 0 || GapWrap.x == 0 || GapWrap.y == 0) yield break;
                init = true;
            }

            var tmpCenter = Center(tmpPosition);

            // Debug.LogError("update center:"+position+":"+tmpPosition+":"+waitUpdate);
            if (waitUpdate) yield break;
            waitUpdate = true;
            updateCenter = true;

            var newSet = new List<Vector2Int>();
            var tmp = new Block { Gap = Gap, GapWrap = GapWrap };

            position = tmpPosition;
            center = tmpCenter;
            yield return _UpdateBefore();


            var e = BlockTools.BlockList.GetEnumerator();
            while (e.MoveNext()) yield return _Add(tmp, e.Current, newSet);
            e.Dispose();

            yield return _Replace(tmp, newSet);
            yield return _UpdateAfter();

            waitUpdate = false;
            if (updateInit)
                yield return UpdateInline();
            else
                yield return UpdateCenter(currentPosition);
        }

        private IEnumerator _Keep()
        {
            // Debug.Log("keep");
            if (_notice != null) yield return _notice.OnKeep(center);
            if (OnKeepEvent != null) yield return OnKeepEvent(center);
        }

        private IEnumerator _UpdateBefore()
        {
            // Debug.Log("before");
            if (_notice != null) yield return _notice.OnUpdateBefore(center);
            if (OnUpdateBeforeEvent != null) yield return OnUpdateBeforeEvent(center);
        }

        private IEnumerator _UpdateAfter()
        {
            // Debug.Log("after");
            if (_notice != null) yield return _notice.OnUpdateAfter(center);
            if (OnUpdateAfterEvent != null) yield return OnUpdateAfterEvent(center);
        }

        private IEnumerator _Add(Block tmp, BlockDirection direction, List<Vector2Int> newSet)
        {
            center.ToDirection(direction, ref tmp);

            newSet.Add(tmp.Position);
            if (currentSet.Contains(tmp.Position)) yield break;
            if (_notice != null) yield return _notice.OnAddBlock(tmp);
            if (OnAddBlockEvent != null) yield return OnAddBlockEvent(tmp);
        }

        private IEnumerator _Replace(Block tmp, List<Vector2Int> newSet)
        {
            for (var i = 0; i < currentSet.Count; i++)
            {
                var p = currentSet[i];
                if (newSet.Contains(p)) continue;

                tmp.UpdatePosition(p);
                if (_notice != null) yield return _notice.OnRemoveBlock(tmp);
                if (OnRemoveBlockEvent != null) yield return OnRemoveBlockEvent(tmp);
            }

            currentSet.Clear();
            currentSet.AddRange(newSet);
            newSet.Clear();
        }

        private IEnumerator _DiffNotice(Vector2Int point, int width, int height, bool p)
        {
            var tmp = new Block();
            tmp.FromRect(new RectInt(point.x, point.y, width, height));
            if (p)
            {
                if (_notice != null) yield return _notice.OnAddBlock(tmp);
                if (OnAddBlockEvent != null) yield return OnAddBlockEvent(tmp);
            }
            else
            {
                if (_notice != null) yield return _notice.OnRemoveBlock(tmp);
                if (OnRemoveBlockEvent != null) yield return OnRemoveBlockEvent(tmp);
            }
        }

        private IEnumerator _Diff(Block oldCenter, Block newCenter)
        {
            var oldRect = oldCenter.ToRectInt(3);
            var newRect = newCenter.ToRectInt(3);
            // Debug.LogError(oldRect+":"+newRect);
            var p = false;
            RectInt one;
            RectInt two;
            var tmp = new Block();
            if (oldRect.xMin != newRect.xMin)
            {
                // 左下
                if (oldRect.xMin > newRect.xMin)
                {
                    p = true;
                    one = newRect;
                    two = oldRect;
                }
                else
                {
                    p = false;
                    one = oldRect;
                    two = newRect;
                }

                var point = new Vector2Int(one.xMin, one.yMin);
                var width = two.xMin - one.xMin;
                var height = Math.Abs(Math.Min(two.yMax, one.yMax) - one.yMin);
                // Debug.LogError(point+":"+width+":"+height);
                yield return _DiffNotice(point, width, height, p);
            }

            if (oldRect.yMax != newRect.yMax)
            {
                // 左上
                if (oldRect.yMax < newRect.yMax)
                {
                    p = true;
                    one = newRect;
                    two = oldRect;
                }
                else
                {
                    p = false;
                    one = oldRect;
                    two = newRect;
                }

                var width = Math.Abs(Math.Min(two.xMax, one.xMax) - one.xMin);
                var height = one.yMax - two.yMax;
                var point = new Vector2Int(one.xMin, one.yMax - height);
                yield return _DiffNotice(point, width, height, p);
            }

            if (oldRect.xMax != newRect.xMax)
            {
                // 右上
                if (oldRect.xMax < newRect.xMax)
                {
                    p = true;
                    one = newRect;
                    two = oldRect;
                }
                else
                {
                    p = false;
                    one = oldRect;
                    two = newRect;
                }

                var width = one.xMax - two.xMax;
                var height = Math.Abs(Math.Max(two.yMin, one.yMin) - one.yMax);
                var point = new Vector2Int(one.xMax - width, one.yMax - height);
                yield return _DiffNotice(point, width, height, p);
            }

            if (oldRect.yMin != newRect.yMin)
            {
                if (oldRect.yMin > newRect.yMin)
                {
                    p = true;
                    one = newRect;
                    two = oldRect;
                }
                else
                {
                    p = false;
                    one = oldRect;
                    two = newRect;
                }

                var width = Math.Abs(Math.Max(two.xMin, one.xMin) - one.xMax);
                var height = two.yMin - one.yMin;
                var point = new Vector2Int(one.xMax - width, one.yMin);
                yield return _DiffNotice(point, width, height, p);
            }
        }

        public IEnumerator UpdateInline()
        {
            waitUpdate = true;
            updateInit = false;
            var oldCenter = center;
            center = Center(position);
            yield return _UpdateBefore();
            var notice = _notice;
            var addEvent = OnAddBlockEvent;
            var removeEvent = OnRemoveBlockEvent;
            _notice = null;
            OnAddBlockEvent = null;
            OnRemoveBlockEvent = null;

            var newSet = new List<Vector2Int>();
            var tmp = new Block { Gap = Gap, GapWrap = GapWrap };

            var e = BlockTools.BlockList.GetEnumerator();
            while (e.MoveNext()) yield return _Add(tmp, e.Current, newSet);
            e.Dispose();

            yield return _Replace(tmp, newSet);

            _notice = notice;
            OnAddBlockEvent = addEvent;
            OnRemoveBlockEvent = removeEvent;

            Middle = new Block();
            Middle.GapWrap = GapWrap;
            Middle.Gap = Gap;

            yield return _Diff(oldCenter, center);

            yield return _UpdateAfter();
            waitUpdate = false;
            if (updateInit)
                yield return UpdateInline();
            else if (updateCenter)
                yield return UpdateCenter(currentPosition);
            else if (updateSimple) yield return UpdateSimple(currentPosition);
        }

        public bool Contains(Vector3Int position, int number)
        {
            return center.Contain(position, number);
        }

        public void Init(int Gap, int Wrap)
        {
            Init(Gap, Gap, Wrap, Wrap, null);
        }

        public IEnumerator Update(int Gap, int Wrap)
        {
            yield return Update(Gap, Gap, Wrap, Wrap);
        }

        public void Init(int xGap, int zGap, int Wrap)
        {
            Init(xGap, zGap, Wrap, Wrap, null);
        }

        public IEnumerator Update(int xGap, int zGap, int Wrap)
        {
            yield return Update(xGap, zGap, Wrap, Wrap);
        }

        public void Init(int xGap, int zGap, int Wrap, BlockNotice notice)
        {
            Init(xGap, zGap, Wrap, Wrap, notice);
        }

        public void Init(int xGap, int zGap, int xWrap, int zWrap)
        {
            Init(xGap, zGap, xWrap, zWrap, null);
        }

        public void Init(int xGap, int zGap, int xWrap, int zWrap, BlockNotice notice)
        {
            Gap.x = xGap;
            Gap.y = zGap;
            GapWrap.x = xGap + xWrap;
            GapWrap.y = zGap + zWrap;
            _notice = notice;
            Middle = new Block();
            Middle.GapWrap = GapWrap;
            Middle.Gap = Gap;
        }

        public IEnumerator Update(int xGap, int zGap, int xWrap, int zWrap)
        {
            if (!init) yield break;
            Gap.x = xGap;
            Gap.y = zGap;
            GapWrap.x = xGap + xWrap;
            GapWrap.y = zGap + zWrap;
            if (waitUpdate)
            {
                updateInit = true;
                yield break;
            }

            yield return UpdateInline();
        }

        public Block Location(int x, int z)
        {
            var block = new Block();
            block.GapWrap = GapWrap;
            block.Gap = Gap;
            block.Position = new Vector2Int(x > 0 ? x * 2 - 1 : x * 2 + 1, z > 0 ? z * 2 - 1 : z * 2 + 1);
            block.Center.x = block.Position.x * Gap.x;
            block.Center.z = block.Position.y * Gap.y;
            return block;
        }

        public Block Center(Vector3 location)
        {
            var b = new Block();
            if (Gap.x == 0 && Gap.y == 0)
            {
                b.Center.x = 0;
                b.Center.z = 0;
                b.Position.x = 0;
                b.Position.y = 0;
                return b;
            }

            // Debug.LogError("center"+":"+position);
            var r = Convert.ToInt32(location.x / Gap.x);
            b.GapWrap = GapWrap;
            b.Gap = Gap;
            // b.Position.x = r + (r % 2 == 0 ? (location.x >= 0 ? 1 : -1) : 0);
            // r = Convert.ToInt32(location.z / zGap);
            // b.Position.y = r + (r % 2 == 0 ? (location.y >= 0 ? 1 : -1) : 0);
            b.Position.x = r + (r % 2 != 0 ? location.x >= 0 ? 1 : -1 : 0);
            r = Convert.ToInt32(location.z / Gap.y);
            b.Position.y = r + (r % 2 != 0 ? location.y >= 0 ? 1 : -1 : 0);
            b.Center = new Vector3Int(Convert.ToInt32(b.Position.x * Gap.x), 0, Convert.ToInt32(b.Position.y * Gap.y));

            return b;
        }

        public Block Center(Vector2Int location)
        {
            var b = new Block();
            if (Gap.x == 0 && Gap.y == 0)
            {
                b.Center.x = 0;
                b.Center.z = 0;
                b.Position.x = 0;
                b.Position.y = 0;
                return b;
            }

            var r = location.x / Gap.x;
            b.GapWrap = GapWrap;
            b.Gap = Gap;
            b.Position.x = r + (r % 2 == 0 ? location.x >= 0 ? 1 : -1 : 0);
            r = location.y / Gap.y;
            b.Position.y = r + (r % 2 == 0 ? location.y >= 0 ? 1 : -1 : 0);
            b.Center = new Vector3Int(Convert.ToInt32(b.Position.x * Gap.x), 0, Convert.ToInt32(b.Position.y * Gap.y));
            return b;
        }
    }

    public class BlockSyncManager
    {
        public delegate void BlockDelegate(Block block);

        private BlockNotice _notice;
        public Block center;
        public Vector3 currentPosition;

        public List<Vector2Int> currentSet = new();
        private BlockDirection direction;
        public Vector2Int Gap;
        public Vector2Int GapWrap;
        private bool init;
        public Block Middle;
        public BlockDelegate OnAddBlockEvent;
        public BlockDelegate OnKeepEvent;
        public BlockDelegate OnRemoveBlockEvent;
        public BlockDelegate OnUpdateAfterEvent;

        public BlockDelegate OnUpdateBeforeEvent;
        public Vector3 position;
        private bool updateCenter;
        private bool updateInit;
        private bool updateSimple;
        private bool waitInit = false;
        private bool waitUpdate;

        public void UpdateSimple(Vector3 tmpPosition)
        {
            // 在inline大小连续切换中，导致contain判断影响到最后一次更新条件
            currentPosition = tmpPosition;
            var keepCenter = false;
            if (init)
            {
                if (center.Contain(tmpPosition, true))
                {
                    keepCenter = true;
                    var d = center.Direction(tmpPosition, GapWrap.x - Gap.x, GapWrap.y - Gap.y);
                    if (d == direction
                        || d == BlockDirection.Left
                        || d == BlockDirection.Bottom
                        || d == BlockDirection.Right
                        || d == BlockDirection.Top
                        || d == BlockDirection.Center)
                    {
                        if (waitUpdate) return;
                        _Keep();
                        return;
                    }
                }
            }
            else
            {
                if (Gap.x == 0 || Gap.y == 0 || GapWrap.x == 0 || GapWrap.y == 0) return;
                init = true;
            }

            var tmpCenter = center;
            if (!keepCenter) tmpCenter = Center(tmpPosition);

            if (waitUpdate) return;
            waitUpdate = true;
            updateSimple = true;

            var newSet = new List<Vector2Int>();
            var tmp = new Block { Gap = Gap, GapWrap = GapWrap };

            if (!keepCenter)
            {
                position = tmpPosition;
                center = tmpCenter;
            }

            if (Gap.x == 0 || Gap.y == 0 || GapWrap.x == 0 || GapWrap.y == 0)
            {
            }
            else
            {
                direction = center.Direction(tmpPosition, 0, 0);
                _UpdateBefore();

                BlockDirection[] directions;
                if (BlockTools.BlockSimpleMap.TryGetValue(direction, out directions))
                    for (var i = 0; i < directions.Length; i++)
                        _Add(tmp, directions[i], newSet);

                _Replace(tmp, newSet);
                _UpdateAfter();
            }

            waitUpdate = false;
            if (updateInit) UpdateInline();
        }

        public void UpdateCenter(Vector3 tmpPosition)
        {
            // 在inline大小连续切换中，导致contain判断影响到最后一次更新条件
            currentPosition = tmpPosition;
            if (init)
            {
                // Debug.LogError("[ Block ] inited center:"+center.Contain(tmpPosition, true));
                if (center.Contain(tmpPosition, true))
                {
                    if (waitUpdate) return;
                    _Keep();
                    return;
                }
            }
            else
            {
                if (Gap.x == 0 || Gap.y == 0 || GapWrap.x == 0 || GapWrap.y == 0) return;
                init = true;
            }

            var tmpCenter = Center(tmpPosition);

            // Debug.LogError("[ Block ] update center:" + tmpCenter.Center + ":" + tmpPosition + ":" + waitUpdate);
            if (waitUpdate) return;
            waitUpdate = true;
            updateCenter = true;

            var newSet = new List<Vector2Int>();
            var tmp = new Block { Gap = Gap, GapWrap = GapWrap };

            position = tmpPosition;
            center = tmpCenter;

            if (Gap.x == 0 || Gap.y == 0 || GapWrap.x == 0 || GapWrap.y == 0)
            {
            }
            else
            {
                _UpdateBefore();

                var e = BlockTools.BlockList.GetEnumerator();
                while (e.MoveNext()) _Add(tmp, e.Current, newSet);
                e.Dispose();

                _Replace(tmp, newSet);
                _UpdateAfter();
            }

            waitUpdate = false;
            if (updateInit) UpdateInline();
        }

        private void _Keep()
        {
            // Debug.Log("keep");
            if (_notice != null) _notice.OnKeep(center);
            if (OnKeepEvent != null) OnKeepEvent(center);
        }

        private void _UpdateBefore()
        {
            // Debug.Log("before");
            if (_notice != null) _notice.OnUpdateBefore(center);
            if (OnUpdateBeforeEvent != null) OnUpdateBeforeEvent(center);
        }

        private void _UpdateAfter()
        {
            // Debug.Log("after");
            if (_notice != null) _notice.OnUpdateAfter(center);
            if (OnUpdateAfterEvent != null) OnUpdateAfterEvent(center);
        }

        private void _Add(Block tmp, BlockDirection direction, List<Vector2Int> newSet)
        {
            center.ToDirection(direction, ref tmp);

            newSet.Add(tmp.Position);
            if (currentSet.Contains(tmp.Position)) return;
            if (_notice != null) _notice.OnAddBlock(tmp);
            if (OnAddBlockEvent != null) OnAddBlockEvent(tmp);
        }

        private void _Replace(Block tmp, List<Vector2Int> newSet)
        {
            for (var i = 0; i < currentSet.Count; i++)
            {
                var p = currentSet[i];
                if (newSet.Contains(p)) continue;

                tmp.UpdatePosition(p);
                if (_notice != null) _notice.OnRemoveBlock(tmp);
                if (OnRemoveBlockEvent != null) OnRemoveBlockEvent(tmp);
            }

            currentSet.Clear();
            currentSet.AddRange(newSet);
            newSet.Clear();
        }

        private void _DiffNotice(Vector2Int point, int width, int height, bool p)
        {
            var tmp = new Block();
            tmp.FromRect(new RectInt(point.x, point.y, width, height));
            if (p)
            {
                if (_notice != null) _notice.OnAddBlock(tmp);
                if (OnAddBlockEvent != null) OnAddBlockEvent(tmp);
            }
            else
            {
                if (_notice != null) _notice.OnRemoveBlock(tmp);
                if (OnRemoveBlockEvent != null) OnRemoveBlockEvent(tmp);
            }
        }

        private void _Diff(Block oldCenter, Block newCenter)
        {
            var oldRect = oldCenter.ToRectInt(3);
            var newRect = newCenter.ToRectInt(3);
            // Debug.LogError(oldRect+":"+newRect);
            var p = false;
            RectInt one;
            RectInt two;
            var tmp = new Block();
            if (oldRect.xMin != newRect.xMin)
            {
                // 左下
                if (oldRect.xMin > newRect.xMin)
                {
                    p = true;
                    one = newRect;
                    two = oldRect;
                }
                else
                {
                    p = false;
                    one = oldRect;
                    two = newRect;
                }

                var point = new Vector2Int(one.xMin, one.yMin);
                var width = two.xMin - one.xMin;
                var height = Math.Abs(Math.Min(two.yMax, one.yMax) - one.yMin);
                // Debug.LogError(point+":"+width+":"+height);
                _DiffNotice(point, width, height, p);
            }

            if (oldRect.yMax != newRect.yMax)
            {
                // 左上
                if (oldRect.yMax < newRect.yMax)
                {
                    p = true;
                    one = newRect;
                    two = oldRect;
                }
                else
                {
                    p = false;
                    one = oldRect;
                    two = newRect;
                }

                var width = Math.Abs(Math.Min(two.xMax, one.xMax) - one.xMin);
                var height = one.yMax - two.yMax;
                var point = new Vector2Int(one.xMin, one.yMax - height);
                _DiffNotice(point, width, height, p);
            }

            if (oldRect.xMax != newRect.xMax)
            {
                // 右上
                if (oldRect.xMax < newRect.xMax)
                {
                    p = true;
                    one = newRect;
                    two = oldRect;
                }
                else
                {
                    p = false;
                    one = oldRect;
                    two = newRect;
                }

                var width = one.xMax - two.xMax;
                var height = Math.Abs(Math.Max(two.yMin, one.yMin) - one.yMax);
                var point = new Vector2Int(one.xMax - width, one.yMax - height);
                _DiffNotice(point, width, height, p);
            }

            if (oldRect.yMin != newRect.yMin)
            {
                if (oldRect.yMin > newRect.yMin)
                {
                    p = true;
                    one = newRect;
                    two = oldRect;
                }
                else
                {
                    p = false;
                    one = oldRect;
                    two = newRect;
                }

                var width = Math.Abs(Math.Max(two.xMin, one.xMin) - one.xMax);
                var height = two.yMin - one.yMin;
                var point = new Vector2Int(one.xMax - width, one.yMin);
                _DiffNotice(point, width, height, p);
            }
        }

        public void UpdateInline()
        {
            waitUpdate = true;
            updateInit = false;
            var oldCenter = center;
            center = Center(position);
            _UpdateBefore();
            var notice = _notice;
            var addEvent = OnAddBlockEvent;
            var removeEvent = OnRemoveBlockEvent;
            _notice = null;
            OnAddBlockEvent = null;
            OnRemoveBlockEvent = null;

            var newSet = new List<Vector2Int>();
            var tmp = new Block { Gap = Gap, GapWrap = GapWrap };

            if (Gap.x == 0 || Gap.y == 0 || GapWrap.x == 0 || GapWrap.y == 0)
            {
                _notice = notice;
                OnAddBlockEvent = addEvent;
                OnRemoveBlockEvent = removeEvent;
                tmp.Gap = Middle.Gap;
                tmp.GapWrap = Middle.GapWrap;
                tmp.Center = oldCenter.Center;
            }
            else
            {
                var e = BlockTools.BlockList.GetEnumerator();
                while (e.MoveNext()) _Add(tmp, e.Current, newSet);
                e.Dispose();
            }

            _Replace(tmp, newSet);

            _notice = notice;
            OnAddBlockEvent = addEvent;
            OnRemoveBlockEvent = removeEvent;

            Middle = new Block();
            Middle.GapWrap = GapWrap;
            Middle.Gap = Gap;

            // Debug.LogError("[ Block ]update center:" + oldCenter.Center + "->" + center.Center + " " + currentPosition);
            if (Gap.x == 0 || Gap.y == 0 || GapWrap.x == 0 || GapWrap.y == 0)
            {
            }
            else
            {
                _Diff(oldCenter, center);
            }

            _UpdateAfter();
            waitUpdate = false;
            if (updateInit)
                UpdateInline();
            else if (updateCenter)
                UpdateCenter(currentPosition);
            else if (updateSimple) UpdateSimple(currentPosition);
        }

        public bool Contains(Vector3Int position, int number)
        {
            return center.Contain(position, number);
        }

        public void Init(int Gap, int Wrap)
        {
            Init(Gap, Gap, Wrap, Wrap, null);
        }

        public void Update(int Gap, int Wrap)
        {
            Update(Gap, Gap, Wrap, Wrap);
        }

        public void Init(int xGap, int zGap, int Wrap)
        {
            Init(xGap, zGap, Wrap, Wrap, null);
        }

        public void Update(int xGap, int zGap, int Wrap)
        {
            Update(xGap, zGap, Wrap, Wrap);
        }

        public void Init(int xGap, int zGap, int Wrap, BlockNotice notice)
        {
            Init(xGap, zGap, Wrap, Wrap, notice);
        }

        public void Init(int xGap, int zGap, int xWrap, int zWrap)
        {
            Init(xGap, zGap, xWrap, zWrap, null);
        }

        public void Init(int xGap, int zGap, int xWrap, int zWrap, BlockNotice notice)
        {
            Gap.x = xGap;
            Gap.y = zGap;
            GapWrap.x = xGap + xWrap;
            GapWrap.y = zGap + zWrap;
            _notice = notice;
            Middle = new Block();
            Middle.GapWrap = GapWrap;
            Middle.Gap = Gap;
            init = false;
            currentSet.Clear();
        }

        public void Update(int xGap, int zGap, int xWrap, int zWrap)
        {
            Gap.x = xGap;
            Gap.y = zGap;
            GapWrap.x = xGap + xWrap;
            GapWrap.y = zGap + zWrap;
            if (!init)
            {
                Middle = new Block();
                Middle.GapWrap = GapWrap;
                Middle.Gap = Gap;
                return;
            }

            if (waitUpdate)
            {
                updateInit = true;
                return;
            }

            UpdateInline();
        }

        public Block Location(int x, int z)
        {
            var block = new Block();
            block.GapWrap = GapWrap;
            block.Gap = Gap;
            block.Position = new Vector2Int(x > 0 ? x * 2 - 1 : x * 2 + 1, z > 0 ? z * 2 - 1 : z * 2 + 1);
            block.Center.x = block.Position.x * Gap.x;
            block.Center.z = block.Position.y * Gap.y;
            return block;
        }

        public Block Center(Vector3 location)
        {
            var b = new Block();
            // Debug.LogError("center"+":"+Gap+"->"+GapWrap+":"+location);
            if (Gap.x == 0 && Gap.y == 0)
            {
                b.Center.x = 0;
                b.Center.z = 0;
                b.Position.x = 0;
                b.Position.y = 0;
                return b;
            }

            var r = Convert.ToInt32(location.x / Gap.x);
            b.GapWrap = GapWrap;
            b.Gap = Gap;
            // b.Position.x = r + (r % 2 == 0 ? (location.x >= 0 ? 1 : -1) : 0);
            // r = Convert.ToInt32(location.z / zGap);
            // b.Position.y = r + (r % 2 == 0 ? (location.y >= 0 ? 1 : -1) : 0);
            b.Position.x = r + (r % 2 != 0 ? location.x >= 0 ? 1 : -1 : 0);
            r = Convert.ToInt32(location.z / Gap.y);
            b.Position.y = r + (r % 2 != 0 ? location.y >= 0 ? 1 : -1 : 0);
            b.Center = new Vector3Int(Convert.ToInt32(b.Position.x * Gap.x), 0, Convert.ToInt32(b.Position.y * Gap.y));

            return b;
        }

        public Block Center(Vector2Int location)
        {
            var b = new Block();
            if (Gap.x == 0 && Gap.y == 0)
            {
                b.Center.x = 0;
                b.Center.z = 0;
                b.Position.x = 0;
                b.Position.y = 0;
                return b;
            }

            var r = location.x / Gap.x;
            b.GapWrap = GapWrap;
            b.Gap = Gap;
            b.Position.x = r + (r % 2 == 0 ? location.x >= 0 ? 1 : -1 : 0);
            r = location.y / Gap.y;
            b.Position.y = r + (r % 2 == 0 ? location.y >= 0 ? 1 : -1 : 0);
            b.Center = new Vector3Int(Convert.ToInt32(b.Position.x * Gap.x), 0, Convert.ToInt32(b.Position.y * Gap.y));
            return b;
        }
    }

    public class BlockTools
    {
        internal static List<BlockDirection> BlockList = new()
        {
            BlockDirection.Center,
            BlockDirection.Top,
            BlockDirection.Bottom,
            BlockDirection.Left,
            BlockDirection.Right,
            BlockDirection.LeftTop,
            BlockDirection.LeftBottom,
            BlockDirection.RightTop,
            BlockDirection.RightBottom
        };

        internal static Dictionary<BlockDirection, BlockDirection[]> BlockSimpleMap = new()
        {
            {
                BlockDirection.LeftTop,
                new[] { BlockDirection.Center, BlockDirection.Left, BlockDirection.Top, BlockDirection.LeftTop }
            },
            {
                BlockDirection.LeftBottom,
                new[] { BlockDirection.Center, BlockDirection.Left, BlockDirection.Bottom, BlockDirection.LeftBottom }
            },
            {
                BlockDirection.RightTop,
                new[] { BlockDirection.Center, BlockDirection.Right, BlockDirection.Top, BlockDirection.RightTop }
            },
            {
                BlockDirection.RightBottom,
                new[] { BlockDirection.Center, BlockDirection.Right, BlockDirection.Bottom, BlockDirection.RightBottom }
            }
        };

        internal static Dictionary<string, int> IndexMap = new()
        {
            { "1,1", 0 },
            { "3,1", 1 },
            { "5,1", 2 },
            { "1,3", 3 },
            { "3,3", 4 },
            { "5,3", 5 },
            { "1,5", 6 },
            { "3,5", 7 },
            { "5,5", 8 },
            { "-1,1", 2 },
            { "-3,1", 1 },
            { "-5,1", 0 },
            { "-1,3", 5 },
            { "-3,3", 4 },
            { "-5,3", 3 },
            { "-1,5", 8 },
            { "-3,5", 7 },
            { "-5,5", 6 },
            { "1,-1", 6 },
            { "3,-1", 7 },
            { "5,-1", 8 },
            { "1,-3", 3 },
            { "3,-3", 4 },
            { "5,-3", 5 },
            { "1,-5", 0 },
            { "3,-5", 1 },
            { "5,-5", 2 },
            { "-1,-1", 8 },
            { "-3,-1", 7 },
            { "-5,-1", 6 },
            { "-1,-3", 5 },
            { "-3,-3", 4 },
            { "-5,-3", 3 },
            { "-1,-5", 2 },
            { "-3,-5", 1 },
            { "-5,-5", 0 }
        };

        public static BlockManager Manager { get; } = new();

        public static BlockSyncManager SyncManager { get; } = new();

        internal static int PositionIndex(Vector2 position)
        {
            var i = 0;
            IndexMap.TryGetValue(string.Format("{0},{1}", position.x % 6, position.y % 6), out i);
            // Debug.LogFormat("{0},{1} {2}", position.x % 6, position.y % 6, i);
            return i;
        }
    }
}